package com.example.controllor;

import com.example.BatchJobListener;
import com.example.entity.User;
import com.example.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.batch.core.*;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.batch.item.file.transform.FixedLengthTokenizer;
import org.springframework.batch.item.file.transform.Range;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;

@Slf4j
@RestController
public class TestControllor {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    /**
     * Step构建工厂，用于构建Step
     */
    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    private BatchJobListener batchJobListener;

    @Autowired
    private IUserService iUserService;


    @GetMapping("/test1")
    public String test() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
        log.info("任务执行了...");
        JobParameters jobParameters = new JobParametersBuilder()
                .addString("param1", RandomStringUtils.randomAlphabetic(8).toUpperCase())
                .toJobParameters();

        // 获取job并运行
        Job job = jobBuilderFactory.get("batchJob").incrementer(new RunIdIncrementer())
                // start是JOB执行的第一个step
                .start(startStep())
                // 可以调用next方法设置其他的step，例如：
                //.next(xxxStep())
                // 设置我们自定义的JobListener
                .listener(batchJobListener).build();


        JobExecution execution = jobLauncher.run(job, jobParameters);
        log.info("定时任务结束. Exit Status : {}", execution.getStatus());
        return "success";
    }


    private Step startStep() {

        return stepBuilderFactory.get("readerFromCsvFile")
                // <输入对象, 输出对象>  chunk通俗的讲类似于SQL的commit; 这里表示处理(processor)100条后写入(writer)一次
                .<User, User>chunk(2)

                // 捕捉到异常就重试,重试100次还是异常,JOB就停止并标志失败
                .faultTolerant().retryLimit(3).retry(Exception.class).skipLimit(100).skip(Exception.class).
                // 指定ItemReader对象
                        reader(getCustomDataReader()).
                // 指定ItemProcessor对象
                        processor(getDataProcessor()).
                // 指定ItemWriter对象
                        writer(mybatisPlusUserWriter()).build();
    }


    public FlatFileItemReader<User> getCustomDataReader() {
        FlatFileItemReader<User> reader = new FlatFileItemReader<>();
        reader.setResource(new ClassPathResource("data.dat"));
        reader.setLinesToSkip(0);
        reader.setStrict(false);// 忽略不存在的文件

        reader.setLineMapper(new DefaultLineMapper<User>() {
            {
                setLineTokenizer(new FixedLengthTokenizer() {
                    {
                        setNames("id", "name", "age");
                        setColumns(new Range(1, 2), new Range(3, 12), new Range(13, 15));
                        setStrict(false);
                    }
                });
                setFieldSetMapper(fieldSet -> new User() {{
                    setId(Integer.parseInt(fieldSet.readString("id").trim()));
                    setName(fieldSet.readString("name").trim());
                    setAge(Integer.parseInt(fieldSet.readString("age").trim()));
                }});
            }
        });
        return reader;
    }

    public FlatFileItemReader<User> getCsvDataReader() {
        FlatFileItemReader<User> reader = new FlatFileItemReader<>();
        reader.setResource(new ClassPathResource("data.csv"));
        // 1. 创建LineTokenizer
        DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
        tokenizer.setNames("id", "name", "age");  // 直接传递字符串数组

        // 2. 创建FieldSetMapper
        BeanWrapperFieldSetMapper<User> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
        fieldSetMapper.setTargetType(User.class);

        // 3. 创建LineMapper并组装组件
        DefaultLineMapper<User> lineMapper = new DefaultLineMapper<>();
        lineMapper.setLineTokenizer(tokenizer);
        lineMapper.setFieldSetMapper(fieldSetMapper);

        // 4. 设置LineMapper
        reader.setLineMapper(lineMapper);
        return reader;
    }

    /**
     * 处理数据
     *
     * @return ItemProcessor Object
     */
    private ItemProcessor<User, User> getDataProcessor() {
        return user -> {
            // 模拟处理数据，这里处理就是打印一下
            log.info("processor data : " + user.toString());

            return user;
        };
    }

    /**
     * 写入数据
     *
     * @return ItemWriter Object
     */
    private ItemWriter<User> mybatisPlusUserWriter() {
        return items -> {
            log.info("写入数据库{}", items);
            iUserService.insertBatch(new ArrayList<>(items));
        };
    }
}
